<script setup>
import { computed, watch } from "vue";

defineOptions({
  name: "ByCheckbox",
});

// element plus checkbox
// 父组件v-model绑定值传入的是数组或者字符串,这边自动转换为数组绑定给checkbox组件,更新的值转换为字符串响应式传给父组件
// 如果options只有一个选项,默认选择
const props = defineProps({
  options: {
    type: Array,
    default: () => [],
  },
  valueKey: {
    // 选项的值
    type: String,
    default: "value",
  },
  labelKey: {
    // 显示的名称
    type: [String, Function],
    default: "label",
  },
  modelValue: {
    //  选中值
    type: [String, Array],
    default: () => "",
  },
  modelType: {
    // 返回给父组件的值的数据类型,string或数组
    type: String,
    default: "array",
    validator: (value) => ['string', 'array'].includes(value)
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  size: {
    type: String,
    default: "default",
  },
  border: {
    type: Boolean,
    default: false,
  },
  direction: {
    // 方向
    type: String,
    default: "row",
  },
  prefix: {
    // 前缀,prefix为#in#时,返回给父组件的值为#in#1,2,3...
    type: String,
    default: "",
  },
  showCheckAll: {
    type: Boolean,
    default: false,
  },
  onlyOne: { // 只有一个选项时,直接赋值
    type: Boolean,
    default: false,
  },
  enablePartialCheckAll: Boolean, // 启用部分全选(例如: 搜索导致可操作选项变少,搜索后全选不能覆盖之前的勾选)
  isShowCount: Boolean, // 是否显示勾选数量
});

// 将字符串转换为数组
const modelValues = computed(() => {
  if (props.modelValue == null || props.modelValue == "") {
    return [];
  }
  if (props.modelType === "string") {
    return props.modelValue.replace(props.prefix, "").split(",");
  } else {
    return props.modelValue;
  }
});

const emit = defineEmits(["update:modelValue", "change"]);

// 处理值更新
const handleUpdate = (val) => {
  let data
  if (props.modelType === "string") {
    data = val.length ? props.prefix + val.join(",") : val.join(",");
  } else {
    data = val;
  }
  emit("update:modelValue", data);
};
// 触发更新事件
const handleChange = (val) => {
  emit("change", val); // 触发更新事件
};
const currentOptionsValue = computed(() => {
  return props.options.map((item) => item[props.valueKey]);
})

// 全选状态
const checkAll = computed({
  get() {
    if (!props.options.length) return false;
    if(props.enablePartialCheckAll) {
      return currentOptionsValue.value.every((value) => modelValues.value.includes(value));
    }
    return modelValues.value.length === currentOptionsValue.value.length;
  },
  set(val) {
    if(props.enablePartialCheckAll) {
      // 启用部分全选后,勾选全选把当前所有的选项添加到modelValues中,取消勾选要把当前所有选项从modelValues中移除
      const values = val ? [...new Set([...modelValues.value,...currentOptionsValue.value])] : modelValues.value.filter((value) => !(currentOptionsValue.value.includes(value)));
      handleUpdate(values);
      handleChange(values);
    } else {
      const values = val ? currentOptionsValue.value : [];
      handleUpdate(values);
      handleChange(values);
    }
  },
});

// 是否半选状态
const isIndeterminate = computed(() => {
  let length = modelValues.value.length;
  if(props.enablePartialCheckAll) {
    const matchOptions = modelValues.value.filter((value) => currentOptionsValue.value.includes(value))
    length = matchOptions.length
  }
  return (
    length > 0 &&
    length < props.options.length
  );
});

// 处理全选点击
const handleCheckAllChange = (val) => {
  checkAll.value = val;
};

// 监听options变化，只有一个选项时自动选中
watch(() => props.options, (newOptions) => {
  if (newOptions.length === 1 && props.onlyOne) {
    const value = newOptions[0][props.valueKey]
    handleUpdate([value])
  }
}, { immediate: true, deep: true })

const getLabel = (item) => {
  if (item) {
    let labelKey = props.labelKey;
    if (typeof labelKey === 'function') return labelKey(item)
    if (labelKey) return item[labelKey]
    return props.entityName ? item.name : item.label
  }
}
</script>

<template>
  <div class="by-checkbox-container">
    <!-- 全选区域 -->
    <div v-if="showCheckAll || $slots.topLeft || $slots.topRight" class="by-checkbox-top-operate">
      <div class="left">
        <el-checkbox v-if="showCheckAll" v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">
          全选<span v-if="isShowCount">({{ modelValues.length }})</span>
        </el-checkbox>
        <slot v-if="$slots.topLeft" name="topLeft" />
      </div>
      <div class="right">
        <slot v-if="$slots.topRight" name="topRight" />
      </div>
    </div>
    <!-- 复选框组 -->
    <el-checkbox-group v-if="options.length > 0" :model-value="modelValues" @update:model-value="handleUpdate" @change="handleChange"
      :disabled="disabled" :size="size" :direction="direction" :style="{
        display: 'flex',
        flexDirection: direction === 'row' ? 'row' : 'column',
      }">
      <el-checkbox v-for="(item, index) in options" :key="index" :border="border" :value="item[valueKey]"
        :label="getLabel(item)" :disabled="item.disabled" style="width: fit-content">
        <slot name="default" :row="item">
          {{ getLabel(item) }}
        </slot>
      </el-checkbox>
    </el-checkbox-group>
  </div>
</template>

<style scoped lang="scss">
.by-checkbox-container {
  .el-checkbox-group {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
  }
}
.by-checkbox-top-operate {
  display: flex;
  align-items: center;
  justify-content: space-between;
  .left,.right {
    display: flex;
    align-items: center;
    gap: 10px;
  }
}
</style>
